using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using Comteck.Utils;
using Comteck.Winforms.Forms;
using Comteck.Winforms.Templates;
using Comteck.Winforms.Utils;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Grid;
using WinformApp = System.Windows.Forms.Application;

namespace Comteck.Winforms.Dialog {

  /// <summary>
  /// 数据导入
  /// </summary>
  public partial class FrmExcelImportDialog : FrmBase {
    #region fields
    private OpenFileDialog openFileDialog1 = null;
    /// <summary>
    /// 是否需要完全展示所有列
    /// </summary>
    public bool BestFitColumns { get; set; } = true;
    /// <summary>
    /// 对应使用的模板
    /// </summary>
    public EnumTemplateType TemplateType { get; set; } = EnumTemplateType.Unknow;
    /// <summary>
    /// 是否所有数据都有效才能导入
    /// </summary>
    public bool AllValid = false;
    /// <summary>
    /// 当前的模板
    /// </summary>
    private ImportTemplateDto currentTemplate = null;
    /// <summary>
    /// 当前模板的列数
    /// </summary>
    private int currentTemplate_ColumnsCount = 0;

    /// <summary>
    /// 是否校验列数匹配
    /// </summary>
    public bool CheckTemplateColumnsCount { get; set; } = true;
    /// <summary>
    /// 合计栏
    /// </summary>
    public List<GridColumnSummaryItem> SummaryList { get; set; }
    /// <summary>
    /// 显示掩码：key=FieldName，value=DisplayFormatString
    /// </summary>
    public Dictionary<string, string> ColumnDic { get; set; }
    public Action<GridView> RefreshGridViewLayoutAction;
    #endregion

    #region delegate and events

    #region 确定并导入
    /// <summary>
    /// 【确定并导入】 的委托(同步)
    /// </summary>
    /// <param name="data">GridControl的数据源</param>
    public delegate bool SaveExcelDataHandle(object data, GridView gridView);
    /// <summary>
    /// 【确定并导入】 的事件(同步)
    /// </summary>
    public event SaveExcelDataHandle OnSaveExcelData;
    /// <summary>
    /// 【确定并导入】 的委托(异步)
    /// </summary>
    /// <param name="data">GridControl的数据源</param>
    public delegate Task<bool> SaveExcelDataAsyncHandle(object data, GridView gridView);
    /// <summary>
    /// 【确定并导入】 的事件(异步)
    /// </summary>
    public event SaveExcelDataAsyncHandle OnSaveExcelDataAsync;
    public Action<GridView> RenderGridViewHandle;
    #endregion

    #region 改变显示的数据源

    /// <summary>
    /// 【数据源改变】 的委托(同步)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="table"></param>
    /// <returns></returns>
    public delegate object ChangeExcelDataHandle(DataTable table, GridView gridView);

    /// <summary>
    /// 【数据源改变】 的事件(同步)
    /// </summary>
    public event ChangeExcelDataHandle OnChangeExcelData;

    /// <summary>
    /// 【数据源改变】 的委托(异步)
    /// </summary>
    /// <param name="data">GridControl的数据源</param>
    public delegate Task<object> ChangeExcelDataAsyncHandle(DataTable table, GridView gridView);

    /// <summary>
    /// 【数据源改变】 的事件(异步)
    /// </summary>
    public event ChangeExcelDataAsyncHandle OnChangeExcelDataAsync;

    #endregion

    #region ValidatingEditor
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void ValidatingEditorHandle(object sender, DevExpress.XtraEditors.Controls.BaseContainerValidateEditorEventArgs e);
    /// <summary>
    /// 
    /// </summary>
    public event ValidatingEditorHandle OnValidatingEditor;

    #endregion

    #endregion

    #region ctor

    /// <summary>
    /// Ctor
    /// </summary>
    public FrmExcelImportDialog() {
      InitializeComponent();
    }

    #endregion

    #region methods

    /// <summary>
    /// 绑定sheet
    /// </summary>
    /// <param name="list"></param>
    /// <param name="setDefault"></param>
    private void BindSheetList(List<string> list, bool setDefault = true) {
      ckSheet.Properties.Items.Clear();
      ckSheet.Properties.Items.AddRange(list);

      if (setDefault && list?.Count > 0) {
        ckSheet.SelectedIndex = 0;
      }
    }

    #endregion

    #region ctor

    /// <summary>
    /// form load
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void FrmExcelImportDialog_Load(object sender, EventArgs e) {
      if (this.DesignMode) {
        return;
      }

      currentTemplate = ExcelTemplateHelper.GetById(this.TemplateType.ToString());

      if (currentTemplate == null) {
        this.ShowMsg($"当前模板文件 {this.TemplateType} 不存在");
        return;
      }

      // 当前模板的列数
      currentTemplate_ColumnsCount = ExcelHelper.GetExcelColumnsCount(this.GetTemplateFullPath(), 0);

      // 设置名称
      this.ckFileName.Text = $"{Path.GetFileName(currentTemplate.Path)}(共{currentTemplate_ColumnsCount}列)";
      this.gvMain.ViewCaption = Path.GetFileNameWithoutExtension(currentTemplate.Path);

      // 对话框
      openFileDialog1 = new OpenFileDialog();
      openFileDialog1.Multiselect = false;
      // Microsoft Excel files|*.xls;*.xlsx
      openFileDialog1.Filter = $"Microsoft Excel files|*.xlsx;*.xlsm";
      openFileDialog1.RestoreDirectory = true;
      //openFileDialog1.FileName = "comteck.ctk";
      openFileDialog1.Title = "选择文件";
    }

    #endregion

    #region file browser

    /// <summary>
    /// 浏览
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnBrowser_Click(object sender, EventArgs e) {
      if (openFileDialog1.ShowDialog() == DialogResult.Cancel) {
        return;
      }

      // 文件路径
      var filePath = openFileDialog1.FileName;
      ckFilePath.EditValue = filePath;

      // 读取文件
      if (File.Exists(filePath) == false) {
        this.ShowMsg("您导入的文件不存在，请检查！");
        return;
      }

      var sheetList = ExcelHelper.GetSheetList(filePath);

      ckSheet.SelectedIndexChanged -= this.CkSheet_SelectedIndexChanged;
      this.BindSheetList(sheetList);
      this.CkSheet_SelectedIndexChanged(null, null);
      ckSheet.SelectedIndexChanged += this.CkSheet_SelectedIndexChanged;
    }

    /// <summary>
    /// 选择的sheet页发生改变
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void CkSheet_SelectedIndexChanged(object sender, EventArgs e) {
      this.UseWaitCursor = true;

      this.gvMain.Columns.Clear();
      this.gcMain.DataSource = null;

      // 当前选中的sheet页
      var sheetName = ckSheet.SelectedItem.ToString(true);

      if (sheetName.IsNotNullOrEmpty()) {
        // 获取模板文件的列
        var datasource = ExcelHelper.Import2Table(ckFilePath.EditValue.ToString(true), sheetName);

        // 如果导入模板的列数不匹配，则需要提醒
        var importColumnCount = datasource?.Columns?.Count ?? 0;
        if (this.CheckTemplateColumnsCount && currentTemplate_ColumnsCount != importColumnCount) {
          this.ShowMsg($"您导入模板的列数为 {importColumnCount}，系统模板的列数为 {currentTemplate_ColumnsCount} {Environment.NewLine} 两者不匹配，请检查您导入的模板！");
          this.UseWaitCursor = false;
          return;
        }

        // 如果需要转换成自定义显示方式的话
        if (this.OnChangeExcelData == null && this.OnChangeExcelDataAsync == null) {
          this.gcMain.DataSource = datasource;

          this.SetRecordLabel(datasource);
        } else {
          object data = null;
          if (OnChangeExcelData != null) {
            data = this.OnChangeExcelData(datasource, this.gvMain);
          } else {
            data = await this.OnChangeExcelDataAsync(datasource, this.gvMain);
          }

          var type = data.GetType();

          if (type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(List<>)) || type.GetGenericTypeDefinition().Equals(typeof(IList<>)))) {
            // 动态创建列
            gvMain.AddListHeads(type.GenericTypeArguments[0]);
            // 显示掩码
            gvMain.AddDisplayMask(this.ColumnDic);
            // 汇总栏 add by weijie.sun 2019/3/27
            gvMain.AddSummarys(this.SummaryList);

            this.gcMain.DataSource = data;

            this.SetRecordLabel(data);

            RefreshGridViewLayoutAction?.Invoke(gvMain);
          } else if (data is DataTable dataTable) {
            this.gcMain.DataSource = dataTable;

            this.SetRecordLabel(dataTable);

            RefreshGridViewLayoutAction?.Invoke(gvMain);
          } else {
            this.ShowMsg("传入的类型暂时只支持 DataTable 或者 List 类型");
          }
        }

        if (this.BestFitColumns) {
          this.gvMain.BestFitColumns();
        }
      }

      this.RenderGridViewHandle?.Invoke(gvMain);

      this.UseWaitCursor = false;
    }

    #endregion

    #region 设置记录数

    /// <summary>
    /// 设置记录数 datatable
    /// </summary>
    /// <param name="table"></param>
    protected void SetRecordLabel(DataTable table) {
      if (table == null) {
        ckRecord_Total.Text = "0";
        ckRecord_Valid.Text = "0";
        ckRecord_UnValid.Text = "0";
      } else {
        ckRecord_Total.Text = table.Rows.Count.ToString();
        ckRecord_Valid.Text = table.Rows.Count.ToString();
        ckRecord_UnValid.Text = "0";
      }
    }

    /// <summary>
    /// 设置记录数 IList
    /// </summary>
    /// <param name="table"></param>
    protected void SetRecordLabel(object data) {
      // ExcelDto
      var total = 0;
      var valid = 0;

      foreach (var dto in data as System.Collections.IEnumerable) {
        total += 1;

        if (dto is ExcelDto excelDto) {
          if (excelDto.IsValid) {
            valid += 1;
          }
        } else {
          valid += 1;
        }
      }

      ckRecord_Total.Text = total.ToString();
      ckRecord_Valid.Text = valid.ToString();
      ckRecord_UnValid.Text = (total - valid).ToString();
    }

    #endregion

    #region 校验表格

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void gvMain_ValidatingEditor(object sender, DevExpress.XtraEditors.Controls.BaseContainerValidateEditorEventArgs e) {
      if (this.OnValidatingEditor != null) {
        this.OnValidatingEditor.Invoke(sender, e);
      }
    }

    #endregion

    #region 确认并导入

    /// <summary>
    /// 确认并导入
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void btnConfirm_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
      if (AllValid && ckRecord_UnValid.Text.To<int>() > 0) {
        this.ShowMsg("导入数据中有无效数据，请修改Excel后重新导入");
        return;
      }
      var msg = $"当前导入信息如下：{Environment.NewLine}{Environment.NewLine}总记录：{ckRecord_Total.Text}，有效记录：{ckRecord_Valid.Text}，无效记录：{ckRecord_UnValid.Text}{Environment.NewLine}{Environment.NewLine}您确定要导入吗？";

      if (this.ShowMsg(msg, "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1) == DialogResult.No) {
        return;
      }

      if (OnSaveExcelData == null && OnSaveExcelDataAsync == null) {
        this.ShowMsg("Event OnSaveExcelData or OnSaveExcelDataAsync is Null");
        return;
      }

      if (OnValidatingEditor != null && !gvMain.ValidateEditor()) {
        return;
      }

      // 数据源
      gvMain.CloseEditor();
      var data = gcMain.DataSource;

      if (data == null) {
        this.ShowMsg("当前表格中没有任何数据！");
        return;
      }

      // TODO 校验数据列与模板中的列数是否一致
      //if (gvMain.Columns.Count != 3) {
      //  this.ShowMsg($"您导入的Excel文件的数据列与模板中的列数不一致，请确认您是否采用了正确的模板！");
      //  return;
      //}

      // 执行导入事件
      var result = false;

      ajaxLoad.Show();
      if (OnSaveExcelData != null) {
        result = OnSaveExcelData.Invoke(gcMain.DataSource, this.gvMain);
      } else {
        result = await OnSaveExcelDataAsync.Invoke(gcMain.DataSource, this.gvMain);
      }
      ajaxLoad.Hide();

      if (result == true) {
        this.DialogResult = DialogResult.Yes;
      }
    }

    #endregion

    #region 下载模板

    /// <summary>
    /// 下载模板
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnDownload_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
      var filePath = this.GetTemplateFullPath();

      WinformHelper.SaveFile(filePath, "Microsoft Excel files|*.xls;*.xlsx;*.xlsm", true);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    private string GetTemplateFullPath() {
      var path = this.currentTemplate.Path.Replace("/", @"\");
      if (path.StartsWith(@"\") == false) {
        path += @"\";
      }

      var filePath = WinformApp.StartupPath + path;

      return filePath;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ckFileName_Click(object sender, EventArgs e) {
      this.btnDownload.PerformClick();
    }

    #endregion

    #region 关闭

    /// <summary>
    /// 关闭
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void btnClose_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) {
      this.Close();
    }

    #endregion

  }

}
